package br.ufrj.dcc.ad.event;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import br.ufrj.dcc.ad.log.LoggerFactory;
import br.ufrj.dcc.ad.metrics.SystemMetrics;
import br.ufrj.dcc.ad.systems.Cliente;
import br.ufrj.dcc.ad.systems.Servidor;

public class ServerAtendmentEvent extends Event {
	private double tempoProximoEvento;
	private Logger debugLogger = LoggerFactory.getLogger(LoggerFactory.Type.DEBUG);
	/**
	 * Instancia o evento responsável pelo controle do consumo no servidor.
	 * @param cliente O cliente que será enviado ao servidor.
	 * @param servidor O servidor que será tratado.
	 * @param tempoOcorrencia O tempo de chegada do cliente no servidor.
	 */
	public ServerAtendmentEvent(Cliente gerador, Servidor servidor, double tempoOcorrencia) {
		super(gerador,servidor,tempoOcorrencia);
	}

	@Override
	public List<Event> executaAcao() {
		List<Event> eventosIvocacao = new ArrayList<Event>();
		double tempoOcorrenciaProximoEventoASerGerado = 0.0;
		double deslocamentoTempo = 0.0;
		
		//Se é o primeiro ServerAtendmentEvent do cliente após sair de uma fila...
		if(gerador.getTempoUltimaEntradaServidor() == null){
			//Atualizar metricas.
			gerador.setTempoUltimaEntradaServidor(tempoOcorrencia);
			gerador.addTempoEstadaFila(gerador.getTipoCliente(), tempoOcorrencia - gerador.getTemposUltimasEntradasFilas().get(gerador.getTipoCliente()));
		}
		//Atualiza o cliente no servidor.
		servidor.setConsumidor(gerador);
		//Atende o cliente até o tempo do próximo evento ou até o fim do tempo necessário.
		deslocamentoTempo = servidor.trabalhaCargaAtual(tempoOcorrencia,tempoProximoEvento);
		tempoOcorrenciaProximoEventoASerGerado = tempoOcorrencia + deslocamentoTempo;
		
		//Se o cliente terminou o seu consumo...
		if((gerador.getTempoConsumoRestante() == 0.0)){
			debugLogger.info(gerador.getNome() + " finalizou consumo pela fila " + gerador.getTipoCliente() +
					" no tempo " + (tempoOcorrenciaProximoEventoASerGerado));
			
			//Limpa o tempo de entrada no servidor.
			gerador.setTempoUltimaEntradaServidor(null);
			//Se o cliente está saindo do sistema, guardar suas métricas.
			if(gerador.getTipoCliente() == (servidor.getFilasDePrioridade().size()-1)){
				SystemMetrics.addMetricasSistema(gerador.getTemposNoServidor(),gerador.getTemposVaosNoServidor(),gerador.getTemposNasFilas());
				//Atualiza número de clientes no sistema e número de clientes finalizados da rodada:
				SystemMetrics.setNumeroClientesSistema(SystemMetrics.getNumeroClientesSistema() - 1);
				SystemMetrics.setNumeroClientesFinalizados(SystemMetrics.getNumeroClientesFinalizados() + 1);
			}
			//Libera o servidor.
			servidor.setConsumidor(null);
			//Incluir próximo cliente no servidor, caso haja.
			Cliente proximoCliente = servidor.getAndRemoveProximoCliente();
			if(proximoCliente != null){
				ServerAtendmentEvent serverAtendmentEvent = new ServerAtendmentEvent(proximoCliente, servidor, tempoOcorrenciaProximoEventoASerGerado); 
				eventosIvocacao.add(serverAtendmentEvent);
				//Pode ser que existam outros eventos de consumo agendados 
				//para clientes com menor prioridade que este, estes devem ser cancelados.
				eventosIvocacao.add(new CancelScheduledAtendmentsEvent(proximoCliente,serverAtendmentEvent,servidor,tempoOcorrenciaProximoEventoASerGerado));
			}
			//Se o cliente possui uma próxima fila para ir.
			if ((gerador.getTipoCliente() < servidor.getFilasDePrioridade().size() - 1)){
				//Sete o tipo dele para a proxima fila.
				gerador.setTipoCliente(gerador.getTipoCliente() + 1);
				//DONE: Sortea o tempo de consumo da próxima fila e seta no cliente.
				try{
					gerador.setTempoConsumoRestante(servidor.geraAmostra(gerador.getTipoCliente()));
					gerador.addTempoConsumoServidor(gerador.getTempoConsumoRestante());
					debugLogger.info(gerador.getNome() + " necessitará de " + gerador.getTempoConsumoRestante() + " para finalizar o seu servico na fila " + gerador.getTipoCliente());
				}catch(Exception e){
					e.printStackTrace();
				}
				//Inclui um novo evento de chegada imediata(após consumo) na próxima fila.
				eventosIvocacao.add(new ArriveEvent(gerador, servidor, tempoOcorrenciaProximoEventoASerGerado));
			}
		}else{
			//O fato aqui retratado não é uma reentrada. 
			//E sim um deslocamento do tempo até o próximo evento que ocorre antes do fim do consumo total do cliente. 
			//Cria um novo evento de consumo, pois o cliente ainda não terminou seu tempo.
			eventosIvocacao.add(new ServerAtendmentEvent(gerador, servidor, tempoOcorrenciaProximoEventoASerGerado));
		}
		return eventosIvocacao;
	}
	
	public double getTempoProximoEvento() {
		return tempoProximoEvento;
	}

	public void setTempoProximoEvento(double tempoProximoEvento) {
		this.tempoProximoEvento = tempoProximoEvento;
	}
}
